#include <cfloat>

#include "TriangleAccessor.h"
#include "Model.h"

using namespace DirectX;

TriangleAccessor::TriangleAccessor(Model* modelAccessed, SimpleVertex* pointA, SimpleVertex* pointB, SimpleVertex* pointC)
	: m_modelAccessed(modelAccessed),
	m_lowerBounds(DirectX::XMFLOAT3(FLT_MAX, FLT_MAX, FLT_MAX)),
	m_upperBounds(DirectX::XMFLOAT3(-FLT_MAX, -FLT_MAX, -FLT_MAX))	// FLT_MIN returns the SMALLEST value, not the LOWEST!
	//m_upperBounds(DirectX::XMFLOAT3(FLT_MIN, FLT_MIN, FLT_MIN))
{
	m_vertices[0] = pointA;
	m_vertices[1] = pointB;
	m_vertices[2] = pointC;
	for(int i = 0; i < 3; ++i)
	{
		DirectX::XMVECTOR location = DirectX::XMLoadFloat3(&m_vertices[i]->Pos);
		location = XMVector3Transform(location, m_modelAccessed->GetRenderData()->Transform);
		DirectX::XMStoreFloat3(m_realPositions + i, location);
		if(m_realPositions[i].x < m_lowerBounds.x)
		{
			m_lowerBounds.x = m_realPositions[i].x;
		}
		if(m_realPositions[i].y < m_lowerBounds.y)
		{
			m_lowerBounds.y = m_realPositions[i].y;
		}
		if(m_realPositions[i].z < m_lowerBounds.z)
		{
			m_lowerBounds.z = m_realPositions[i].z;
		}
		if(m_realPositions[i].x > m_upperBounds.x)
		{
			m_upperBounds.x = m_realPositions[i].x;
		}
		if(m_realPositions[i].y > m_upperBounds.y)
		{
			m_upperBounds.y = m_realPositions[i].y;
		}
		if(m_realPositions[i].z > m_upperBounds.z)
		{
			m_upperBounds.z = m_realPositions[i].z;
		}
	}
	m_centroid = DirectX::XMFLOAT3((m_realPositions[0].x + m_realPositions[1].x + m_realPositions[2].x) / 3.f,
		(m_realPositions[0].y + m_realPositions[1].y + m_realPositions[2].y) / 3.f,
		(m_realPositions[0].z + m_realPositions[1].z + m_realPositions[2].z) / 3.f);

}

Model* TriangleAccessor::GetModelAccessed( void )
{
	return m_modelAccessed;
}

DirectX::XMFLOAT3* TriangleAccessor::GetRealPositions( void )
{
	return m_realPositions;
}

void TriangleAccessor::HighLight( bool highlight )
{
	float materialLightIntensity = highlight ? 1.0f : 0.0f;//set material light to whit in case of highlight, else black(=noninfluencing)
	m_vertices[0]->Color = DirectX::XMFLOAT3(materialLightIntensity, materialLightIntensity, materialLightIntensity);
}

DirectX::XMFLOAT3 TriangleAccessor::GetLowerBounds( void ) const
{
	return m_lowerBounds;
}

DirectX::XMFLOAT3 TriangleAccessor::GetUpperBounds( void ) const
{
	return m_upperBounds;
}

DirectX::XMFLOAT3 TriangleAccessor::GetCentroid( void ) const
{
	return m_centroid;
}

RayTriangleIntersection TriangleAccessor::IntersectWithRay( const DirectX::XMFLOAT3& rayOrigin, const DirectX::XMFLOAT3& rayDirection )
{
	RayTriangleIntersection intersection;
	XMVECTOR p0 = XMLoadFloat3(&m_realPositions[0]);
	XMVECTOR p1 = XMLoadFloat3(&m_realPositions[1]);
	XMVECTOR p2 = XMLoadFloat3(&m_realPositions[2]);
	XMVECTOR o = XMLoadFloat3(&rayOrigin);
	XMVECTOR d = XMLoadFloat3(&rayDirection);
	XMVECTOR e1 = p1 - p0;
	XMVECTOR e2 = p2 - p0;
	XMVECTOR s = o - p0;
	XMVECTOR q = XMVector3Cross(d, e2);
	XMVECTOR r = XMVector3Cross(s, e1);
	XMVECTOR test = XMVector3Dot(r, e2);
	if(XMVector3Dot(q, e1).m128_f32[0] == 0)
	{
		intersection.intersecting = false;
		return intersection;
	}
	XMVECTOR tuv = XMVectorSet(XMVector3Dot(r, e2).m128_f32[0],
		XMVector3Dot(q, s).m128_f32[0],
		XMVector3Dot(r, d).m128_f32[0],
		0);
	tuv /= XMVector3Dot(q, e1).m128_f32[0];
	XMVECTOR intersectionPoint = o + tuv.m128_f32[0] * d;

	
	XMStoreFloat3(&intersection.trianglePoints[0], p0);
	XMStoreFloat3(&intersection.trianglePoints[1], p1);
	XMStoreFloat3(&intersection.trianglePoints[2], p2);
	XMStoreFloat3(&intersection.rayOrigin, o);
	XMStoreFloat3(&intersection.rayDirection, d);
	XMStoreFloat3(&intersection.tuv, tuv);
	XMStoreFloat3(&intersection.intersectionPoint, intersectionPoint);
	intersection.intersecting = intersection.tuv.x >= 0.f
		&& intersection.tuv.y >= 0.f && intersection.tuv.y <= 1.f
		&& intersection.tuv.z >= 0.f && intersection.tuv.z <= 1.f;
	return intersection;
}